/*
 Copyright (C) 2010 Kristian Duske

 This file is part of TrenchBroom.

 TrenchBroom is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 TrenchBroom is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with TrenchBroom. If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

#include "Color.h"
#include "render/IndexRangeMap.h"
#include "render/Renderable.h"
#include "render/VertexArray.h"

#include <memory>

namespace tb::render
{
class BrushIndexArray;
class BrushVertexArray;
class RenderBatch;

class EdgeRenderer
{
public:
  struct Params
  {
    float width;
    double offset;
    bool onTop;
    bool useColor;
    Color color;

    Params(float i_width, double i_offset, bool i_onTop);
    Params(float i_width, double i_offset, bool i_onTop, Color i_color);
    Params(float i_width, double i_offset, bool i_onTop, bool i_useColor, Color i_color);
  };

  class RenderBase
  {
  private:
    const Params m_params;

  public:
    explicit RenderBase(Params params);
    virtual ~RenderBase();

  protected:
    void renderEdges(RenderContext& renderContext);

  private:
    virtual void doRenderVertices(RenderContext& renderContext) = 0;
  };

public:
  virtual ~EdgeRenderer();

  void render(RenderBatch& renderBatch, float width = 1.0f, double offset = 0.0);
  void render(
    RenderBatch& renderBatch,
    const Color& color,
    float width = 1.0f,
    double offset = 0.0);
  void render(
    RenderBatch& renderBatch,
    bool useColor,
    const Color& color,
    float width = 1.0f,
    double offset = 0.0);
  void renderOnTop(RenderBatch& renderBatch, float width = 1.0f, double offset = 0.2);
  void renderOnTop(
    RenderBatch& renderBatch,
    const Color& color,
    float width = 1.0f,
    double offset = 0.2);
  void renderOnTop(
    RenderBatch& renderBatch,
    bool useColor,
    const Color& color,
    float width = 1.0f,
    double offset = 0.2);
  void render(
    RenderBatch& renderBatch,
    bool useColor,
    const Color& color,
    bool onTop,
    float width,
    double offset);

private:
  virtual void doRender(RenderBatch& renderBatch, const Params& params) = 0;
};

class DirectEdgeRenderer : public EdgeRenderer
{
private:
  class Render : public RenderBase, public DirectRenderable
  {
  private:
    VertexArray m_vertexArray;
    IndexRangeMap m_indexRanges;

  public:
    Render(const Params& params, VertexArray& vertexArray, IndexRangeMap& indexRanges);

  private:
    void doPrepareVertices(VboManager& vboManager) override;
    void doRender(RenderContext& renderContext) override;
    void doRenderVertices(RenderContext& renderContext) override;
  };

private:
  VertexArray m_vertexArray;
  IndexRangeMap m_indexRanges;

public:
  DirectEdgeRenderer();
  DirectEdgeRenderer(VertexArray vertexArray, IndexRangeMap indexRanges);
  DirectEdgeRenderer(VertexArray vertexArray, PrimType primType);

private:
  void doRender(RenderBatch& renderBatch, const EdgeRenderer::Params& params) override;
};

class IndexedEdgeRenderer : public EdgeRenderer
{
private:
  class Render : public RenderBase, public IndexedRenderable
  {
  private:
    std::shared_ptr<BrushVertexArray> m_vertexArray;
    std::shared_ptr<BrushIndexArray> m_indexArray;

  public:
    Render(
      const Params& params,
      std::shared_ptr<BrushVertexArray> vertexArray,
      std::shared_ptr<BrushIndexArray> indexArray);

  private:
    void prepareVerticesAndIndices(VboManager& vboManager) override;
    void doRender(RenderContext& renderContext) override;
    void doRenderVertices(RenderContext& renderContext) override;
  };

private:
  std::shared_ptr<BrushVertexArray> m_vertexArray;
  std::shared_ptr<BrushIndexArray> m_indexArray;

public:
  IndexedEdgeRenderer();
  IndexedEdgeRenderer(
    std::shared_ptr<BrushVertexArray> vertexArray,
    std::shared_ptr<BrushIndexArray> indexArray);

private:
  void doRender(RenderBatch& renderBatch, const EdgeRenderer::Params& params) override;
};

} // namespace tb::render
